package tools.encrypt;

import cn.hutool.core.collection.CollectionUtil;
import lombok.SneakyThrows;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @Author: SunAo
 * @Date: 2020/11/20 17:38
 * @Description:
 */
public class Encrypt<T> {

    private T data;

    private int deep = 0;

    private int MAX_DEEP = 50;

    private Encrypt(T data) {
        this.data = data;
    }

    public static <T> Encrypt<T> create(T data){
        return new Encrypt<T>(data);
    }

    public void crypt(){
        crypt(data);
    }
    @SneakyThrows
    private <T> void crypt(T data){
        if(data instanceof Collection){
            ((Collection) data).forEach(item->crypt(item));
        }else if(data instanceof Map){
            ((Map) data).keySet().forEach(key->crypt(((Map) data).get(key)));
        }else{
            cryptObject(data);
        }
    }

    /**
     *  脱敏一个POJO对象
     * @param data
     * @param <T>
     * @return
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private <T> void cryptObject(T data) throws Exception {
        deep++;
        if(deep > MAX_DEEP){
            throw new Exception("Over Max Deep");
        }
        Class<? extends Object> clazz = data.getClass();
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            boolean isZeroParameter = method.getParameterCount() == 0;
            boolean isGetStartedMethodName = method.getName().startsWith("get");
            boolean isReturnVoid = method.getReturnType().getName().equals("void");
            boolean notBaseType = method.getReturnType().getName().contains(".");
            if(isZeroParameter && isGetStartedMethodName && !isReturnVoid && notBaseType) {
                System.out.println("method return type:"+method.getReturnType().getName());
                if(method.getReturnType().equals(String.class)){
                    FieldTypeEnums fieldType = FieldType.getFieldTypeByMethodName(method.getName());
                    String value = null;
                    if(fieldType != null && (value = (String) method.invoke(data)) != null){
                        Method setMethod = clazz.getMethod(method.getName().replace("get", "set"), String.class);
                        switch (fieldType){
                            case MOBILE:
                                setMethod.invoke(data,SensitiveConvert.convertMobile(1,value));
                                break;
                            case ADDRESS:
                                setMethod.invoke(data,SensitiveConvert.convertAddress(1,value));
                                break;
                        }
                    }
                }else if(Collection.class.isAssignableFrom(method.getReturnType())) {
                    Collection obj = (Collection) method.invoke(data);
                    if(CollectionUtil.isNotEmpty(obj)){
                        obj.forEach(item -> crypt(item));
                    }
                }else if(Map.class.isAssignableFrom(method.getReturnType())){
                    Map obj = (Map) method.invoke(data);
                    if(CollectionUtil.isNotEmpty(obj)){
                        obj.keySet().forEach(key->crypt(obj.get(key)));
                    }
                }else if(!method.getReturnType().getName().startsWith("java.")){
                    Object obj = method.invoke(data);
                    if(null != obj){
                        cryptObject(obj);
                    }
                }
            }
        }
    }

    private static class FieldType{
        public static final String[] mobileFiledTypes = {"phone","mobile"};
        public static final String[] addressFiledTypes = {"address"};

        public static FieldTypeEnums getFieldTypeByMethodName(String methodName){
            for (FieldTypeEnums value : FieldTypeEnums.values()) {
                for (String fieldName : value.getFieldNames()) {
                    if(methodName.equalsIgnoreCase("get"+fieldName)){
                        return value;
                    }
                }
            }
            return null;
        }

    }
    private static enum FieldTypeEnums{
        MOBILE(new String[]{"phone", "mobile"}),        // 手机号码
        ADDRESS(new String[]{"address"}),        // 地址
        WECHAT(new String[]{}),        // 微信
        BUSSINESS_WECHAT(new String[]{}),        //企业微信
        EMAIL(new String[]{}),      // 邮件
        TELEPHONE(new String[]{}),      //座机电话
        QQ(new String[]{});     // QQ号码

        private String[] fieldNames;

        FieldTypeEnums(String[] fieldNames) {
            this.fieldNames = fieldNames;
        }

        public String[] getFieldNames() {
            return fieldNames;
        }

        public void setFieldNames(String[] fieldNames) {
            this.fieldNames = fieldNames;
        }
    }

}
